/*
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <QAbstractTextDocumentLayout>
#include <QInputDialog>
#include <QMessageBox>
#include <QScrollBar>
#include <QSettings>
#include <QUrl>
#include <QApplication>

#include "components/chan.h"
#include "chanmanager.h"
#include "log.h"
#include "postdocument.h"
#include "postitemdelegate.h"
#include "postitemview.h"
#include "postwidget.h"
#include "components/thread.h"
#include "threadlistmodel.h"

using Wishmaster::PostId;

PostItemView::PostItemView(QWidget* parent) : QListView(parent),
	m_board(0), m_threadId(),
	m_prevWidget(-1),
	m_fontSizePercents(100),
	m_inMMEvent(false),
	m_adjustSize(true)
{
	setItemDelegate(new PostItemDelegate(this, this));
	setMouseTracking(true);
	m_delete.load(":/wishmaster/images/delete.png");
	m_hide.load(":/wishmaster/images/hide.png");
	m_unhide.load(":/wishmaster/images/unhide.png");
	m_show.load(":/wishmaster/images/zoom_in.png");
	m_show = m_show.scaledToHeight(16, Qt::SmoothTransformation);
	m_info.load(":/wishmaster/images/info.png");
	m_info = m_info.scaledToHeight(16, Qt::SmoothTransformation);

	m_postStyle = PostStylePtr(new PostStyle());
	setStyleSheet(QString("background-color: %1; color: %2; background-image: %3; background-repeat: no-repeat; background-position: bottom right; background-attachment: fixed;").
				  arg(postStyle()->backgroundColor().name()).arg(postStyle()->textColor().name()).
				  arg(postStyle()->background()));
	setVerticalScrollMode(ScrollPerPixel);
}

PostItemView::~PostItemView()
{
	clearWidgets();
	delete itemDelegate();
}

void PostItemView::setThread(const Wishmaster::ThreadPtr& thr)
{
	foreach(PostWidget* w, m_widgets)
	{
		delete w;
	}
	m_widgets.clear();

	m_thread = thr;
	m_board = thr->parent();
	m_threadId = thr->id();

	if(model())
	{
		delete model();
		setModel(new ThreadListModel(m_thread));
	}

	updateContent();
	update();
}

void PostItemView::updateContent()
{
	if(!m_board)
		return;
	if(!m_thread)
		return;
	QFont postFont = defaultPostFont();
	postFont.setPointSize(postFont.pointSize() * fontSizeMultiplier());
	int widgetsCount = m_widgets.size();
	for(int postNumber = 0; postNumber < m_thread->getActualPostsCount(); postNumber++)
	{
		PostPtr post = m_thread->getPost(postNumber);
		if(postNumber == widgetsCount)
		{
			QModelIndex index = model()->index(postNumber, 0);
			PostWidget* w = new PostWidget(m_board, m_threadId, post->id(), this, m_imageHideMode);
			w->setPostStyle(postStyle());
			w->setChainLeader(true);
			m_widgets.append(w);
			widgetsCount++;
			rowsInserted(index, postNumber, postNumber);
			connect(w, SIGNAL(postDelete(PostId)), this, SLOT(postDelete(PostId)));
			connect(w, SIGNAL(postHide(PostId)), this, SLOT(postHide(PostId)));
			connect(w, SIGNAL(postIdClicked(PostId)), this, SLOT(postReply(PostId)));
			connect(w, SIGNAL(postLinkClicked(PostId)), this, SLOT(postLinkClicked(PostId)));
			connect(w, SIGNAL(postDownloadAttachment(PostId, int)), this, SLOT(postDownloadAttachment(PostId, int)));
			w->setWidth(maxItemWidth());
		}
		else if(postNumber > m_widgets.size())
		{
			log(llError, "Desync in PostItemView::updateContent!!!");
		}
	}
	viewport()->update();
}

int PostItemView::maxItemWidth()
{
	return viewport()->size().width() - verticalScrollBar()->size().width() + 2 * spacing() - 32;
}

PostWidget* PostItemView::getWidget(const QModelIndex& index)
{
	if(index.row() < 0 || index.row() >= m_widgets.size())
		return 0; // null
	return m_widgets.at(index.row());
}

void PostItemView::resizeEvent(QResizeEvent* event)
{
	Q_UNUSED(event);
	foreach(PostWidget* w, m_widgets)
	{
		w->setWidth(viewport()->size().width() - verticalScrollBar()->size().width() + 2 * spacing());
	}
}

void PostItemView::changed(int i)
{
	QModelIndex index = model()->index(i, 0);
	PostWidget* w = getWidget(index);
	if(w)
	{
		w->update();
		w->recalcGeometry();
		updateGeometries();
	}
	dataChanged(index, index);
	reset();
}

QString PostItemView::linkAt(const QPoint& p)
{
	QModelIndex index = indexAt(p);
	QRect r = visualRect(index);
	QPoint p2(p.x() - r.x(), p.y() - r.y());
	PostWidget* w = getWidget(index);
	if(w)
	{
		return w->linkAt(p2);
	}
	return QString();
}

QString PostItemView::selectedText(const QPoint& p)
{
	QModelIndex index = indexAt(p);
	QRect r = visualRect(index);
	QPoint p2(p.x() - r.x(), p.y() - r.y());
	PostWidget* w = getWidget(index);
	if(w)
	{
		return w->selectedText(p2);
	}
	return QString();
}

void PostItemView::mousePressEvent(QMouseEvent* event)
{
	QListView::mousePressEvent(event);
	if(event->button() == Qt::RightButton)
		return;
	resetSelections();
	QPoint p = event->pos();
	QModelIndex index = indexAt(p);
	QRect r = visualRect(index);
	QPoint p2(p.x() - r.x(), p.y() - r.y());
	PostWidget* w = getWidget(index);
	if(w)
	{
		w->mousePress(p2);
	}
}

void PostItemView::mouseReleaseEvent(QMouseEvent* event)
{
	mouseMoveEvent(event);
	if(event->button() == Qt::RightButton)
		return;
	QString link = linkAt(event->pos());
	if(!link.isEmpty())
	{
		PostId postid = m_board->getPostID(link, m_threadId);
		Wishmaster::ThreadPtr thread = m_board->getThread(m_threadId);
		int position = thread->getPostPosition(postid);
		if(position < 0)
			return;
		setCurrentIndex(model()->index(position, 0));
		scrollTo(model()->index(position, 0));
	}
	else
	{
		QPoint p = event->pos();
		QModelIndex index = indexAt(p);
		QRect r = visualRect(index);
		QPoint p2(p.x() - r.x(), p.y() - r.y());
		PostWidget* w = getWidget(index);
		if(w)
		{
			w->mouseClick(p2);
		}
	}
	QListView::mouseReleaseEvent(event);
	viewport()->update();
}

void PostItemView::mouseMoveEvent(QMouseEvent* event)
{
	if(m_inMMEvent)
	{
		return;
	}
	m_inMMEvent = true;
	QPoint p = event->pos();
	QModelIndex index = indexAt(p);
	if(index.isValid())
	{
		if(m_prevWidget != index.row())
		{
			PostWidget* w = getWidget(model()->index(m_prevWidget, 0));
			if(w)
			{
				w->mouseLeft();
				update(model()->index(m_prevWidget, 0));
			}
		}
		m_prevWidget = index.row();
		QRect r = visualRect(index);
		QPoint p2(p.x() - r.x(), p.y() - r.y());
		PostWidget* w = getWidget(index);
		if(w)
		{
			w->mouseMove(p2, p);
			update(index);
		}
		QListView::mouseMoveEvent(event);
	}
	m_inMMEvent = false;
}

void PostItemView::wheelEvent(QWheelEvent* event)
{
	if(qApp->keyboardModifiers() & Qt::ControlModifier)
	{
		// TODO do it properly
		if(event->delta() > 0)
			zoom();
		else if(event->delta() < 0)
			unzoom();
	}
	else
	{
		QListView::wheelEvent(event);
	}
}

void PostItemView::keyReleaseEvent(QKeyEvent* event)
{
	if(event->key() == Qt::Key_Minus)
		unzoom();
	else if(event->key() == Qt::Key_Plus)
		zoom();
}

void PostItemView::postDelete(const Wishmaster::PostId& postid)
{
	QSettings settings;
	QString password = QInputDialog::getText(NULL,
			tr(""),
			tr("Enter password"),
			QLineEdit::Password,
			settings.value("posting/defaultPassword", "").toString());
	
	//m_board->getParent()->deletePost(m_board->name(), m_threadId.threadNumericId(), postid.postId(), password);
}

void PostItemView::postHide(const Wishmaster::PostId& postid)
{
	Wishmaster::ThreadPtr thread = m_board->getThread(m_threadId);
	int pos = thread->getPostPosition(postid);
	if(pos < 0)
		return;
	Wishmaster::PostPtr post = thread->lookupPost(postid); //thread->visiblePosts()->lookup(postid);
	if(post->visibility() == Hide)
		post->setVisibility(Unhide);
	else
		post->setVisibility(Hide);
	PostWidget* w = m_widgets.at(pos);
	if(w)
	{
		w->setWidth(viewport()->size().width() - verticalScrollBar()->size().width() + 2 * spacing());
	}
	QModelIndex index = model()->index(pos, 0);
	update(index);
	viewport()->update();
	/*
	rowsAboutToBeRemoved(QModelIndex(), index.row(), index.row());
	thread->hidePost(postid);
		delete w;
		*/
	reset();
}

void PostItemView::postReply(const Wishmaster::PostId& postid)
{
	emit reply(QString(">>%1").arg(postid.postId()));
}

void PostItemView::postLinkClicked(const Wishmaster::PostId& postid)
{
	if(!m_thread)
		return;
	int position = m_thread->getPostPosition(postid);
	if(position < 0)
		return;
	setCurrentIndex(model()->index(position, 0));
	scrollTo(model()->index(position, 0), QAbstractItemView::PositionAtCenter);
}

void PostItemView::postDownloadAttachment(const Wishmaster::PostId& postid, int index)
{
	emit downloadAttachment(postid, index);
}

QImage PostItemView::imageDelete()
{
	return m_delete;
}

QImage PostItemView::imageHide()
{
	return m_hide;
}

QImage PostItemView::imageUnhide()
{
	return m_unhide;
}

QImage PostItemView::imageShow()
{
	return m_show;
}

QImage PostItemView::imageInfo()
{
	return m_info;
}

Wishmaster::Chan* PostItemView::chan()
{
	if(!m_board)
		return 0;
	return m_board->getParent();
}

Wishmaster::Board* PostItemView::board()
{
	return m_board;
}

Wishmaster::ThreadPtr PostItemView::thread()
{
	return m_thread;
}

void PostItemView::resetTexts()
{
	foreach(PostWidget* w, m_widgets)
	{
		w->resetText();
	}
}

void PostItemView::clearWidgets()
{
	foreach(PostWidget* w, m_widgets)
	{
		delete w;
	}
	m_widgets.clear();
}

void PostItemView::resetSelections()
{
	foreach(PostWidget* w, m_widgets)
	{
		w->resetSelection();
	}
}

void PostItemView::setDefaultPostFont(const QFont& font)
{
	m_postStyle->setPostFont(font);
}

QFont PostItemView::defaultPostFont()
{
	return m_postStyle->postFont();
}

void PostItemView::setDefaultHeaderFont(const QFont& font)
{
	m_postStyle->setHeaderFont(font);
}

QFont PostItemView::defaultHeaderFont()
{
	return m_postStyle->headerFont();
}

void PostItemView::setDefaultNoteFont(const QFont& font)
{
	m_postStyle->setNoteFont(font);
}

QFont PostItemView::defaultNoteFont()
{
	return m_postStyle->noteFont();
}

void PostItemView::zoom()
{
	if(m_fontSizePercents >= 500)
		return;
	m_fontSizePercents += 10;
	resetFonts();
}

void PostItemView::unzoom()
{
	if(m_fontSizePercents <= 10)
		return;
	m_fontSizePercents -= 10;
	resetFonts();
}

void PostItemView::setAdjustSize(bool s)
{
	m_adjustSize = s;
	foreach(PostWidget* w, m_widgets)
	{
		w->recalcGeometry();
	}
	QModelIndex start = model()->index(0, 0, QModelIndex());
	QModelIndex end = model()->index(model()->rowCount(), 0, QModelIndex());
	dataChanged(start, end);
	updateGeometries();
	reset();
}

bool PostItemView::isAdjustSize()
{
	return m_adjustSize;
}

void PostItemView::resetFonts()
{
	//QFont postfont = defaultPostFont();
	//postfont.setPointSize(m_defaultPostFontSize * fontSizeMultiplier());
	//setDefaultPostFont(postfont);
	foreach(PostWidget* w, m_widgets)
	{
		w->recalcGeometry();
	}
	QModelIndex start = model()->index(0, 0, QModelIndex());
	QModelIndex end = model()->index(model()->rowCount(), 0, QModelIndex());
	dataChanged(start, end);
	updateGeometries();
	reset();
}

float PostItemView::fontSizeMultiplier()
{
	return m_fontSizePercents / 100.0f;
}

void PostItemView::setImageHideMode(bool s)
{
	m_imageHideMode = s;
	foreach(PostWidget* w, m_widgets)
	{
		w->setImageHideMode(s);
		w->resetText();
		w->recalcGeometry();
	}
	QModelIndex start = model()->index(0, 0, QModelIndex());
	QModelIndex end = model()->index(model()->rowCount(), 0, QModelIndex());
	dataChanged(start, end);
	updateGeometries();
	reset();
}

bool PostItemView::isImageHideMode()
{
	return m_imageHideMode;
}

void PostItemView::setPostStyle(const PostStylePtr& style)
{
	m_postStyle = style;
	setStyleSheet(QString("background-color: %1; color: %2; background-image: %3; background-repeat: no-repeat; background-position: bottom right; background-attachment: fixed;").
				  arg(postStyle()->backgroundColor().name()).arg(postStyle()->textColor().name()).
				  arg(postStyle()->background()));
}

void PostItemView::updateGeometries()
{
	QListView::updateGeometries();
	verticalScrollBar()->setSingleStep(10);
}

PostStylePtr PostItemView::postStyle() const
{
	return m_postStyle;
}

